﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LinAlg3D
{
    /// <summary>
    /// Describes and manipulates a vector in two-dimensional (2-D) space.
    /// </summary>
    [Serializable()]
    public struct Vector2
    {
        private float x, y;

        /// <summary>
        /// Gets and Sets the x component of the-two dimensional vector.
        /// </summary>
        public float X
        {
            get { return x; }
            set { x = value; }
        }

        /// <summary>
        /// Gets and Sets the y component of the two-dimensional vector.
        /// </summary>
        public float Y
        {
            get { return y; }
            set { y = value; }
        }

        /// <summary>
        /// Creates a new Vector2 struct.
        /// </summary>
        /// <param name="x">x component of the two-dimensional vector.</param>
        /// <param name="y">y component of the two-dimensional vector.</param>
        public Vector2(float x, float y)
        {
            this.x = x;
            this.y = y;
        }

        // Properties

        #region Empty
        /// <summary>
        /// Retrieves an empty 2-D vector.
        /// </summary>
        public static Vector2 Empty
        {
            get
            {
                return new Vector2(0, 0);
            }
        }
        #endregion

        // Methods

        #region Addition
        /// <summary>
        /// Adds two 2-D vectors
        /// </summary>
        /// <param name="left">Source Vector2 struct.</param>
        /// <param name="right">Source Vector2 struct.</param>
        /// <returns>Sum of two 2-D vectors.</returns>
        public static Vector2 Add(Vector2 left, Vector2 right)
        {
            return left + right;
        }
        #endregion

        #region Ccw
        public static float Ccw(Vector2 left, Vector2 right)
        {
            Vector3 left3 = new Vector3(left.X, left.Y, 0);
            Vector3 right3 = new Vector3(right.X, right.Y, 0);
            Vector3 res = Vector3.Cross(left3, right3);
            return res.Z;

        }
        #endregion

        #region DotProduct
        /// <summary>
        /// Determines the dot product of two 2-D vectors.
        /// </summary>
        /// <param name="vector1">Source Vector2 structure.</param>
        /// <param name="vector2">Source Vector2 structure.</param>
        /// <returns>A Single value that is the dot product.</returns>
        public static float Dot(Vector2 vector1, Vector2 vector2)
        {
            return vector1.x * vector2.x + vector1.y * vector2.y;
        }
        #endregion

        #region Equals

        /// <summary>
        /// Returns a value that indicates whether the current instance is equal to a specified object.
        /// </summary>
        /// <param name="obj">Object with which to make the comparison.</param>
        /// <returns>Value that is true if the current instance is equal to the specified object, or false if it is not.</returns>
        public override bool Equals(object obj)
        {
            // If parameter is null return false.
            if (obj == null)
            {
                return false;
            }

            // If parameter cannot be cast to Vector2 return false.
            if (!(obj is Vector2))
            {
                return false;
            }

            // Return true if the fields match:
            return (x == ((Vector2)obj).x && y == ((Vector2)obj).y);
        }

        #endregion

        #region GetHashCode
        /// <summary>
        /// Returns the hash code for the current instance.
        /// </summary>
        /// <returns>Hash code for the instance.</returns>
        public override int GetHashCode()
        {
            return x.GetHashCode() ^ y.GetHashCode();
        }
        #endregion

        #region Length
        /// <summary>
        /// Returns the length of a 2-D vector.
        /// </summary>
        /// <param name="vector">Source Vector2 structure.</param>
        /// <returns>A Single value that contains the vector's length.</returns>
        public static float Length(Vector2 vector)
        {
            return vector.Length();
        }

        /// <summary>
        /// Returns the length of a 2-D vector.
        /// </summary>
        /// <returns>A Single value that contains the vector's length.</returns>
        public float Length()
        {
            return (float)System.Math.Sqrt(x * x + y * y);
        }
        #endregion

        #region LengthSquared
        /// <summary>
        /// Returns the square of the length of a 2-D vector.
        /// </summary>
        /// <param name="vector">Source Vector2 structure.</param>
        /// <returns>A Single value that contains the vector's squared length.</returns>
        public static float LengthSquared(Vector2 vector)
        {
            return vector.LengthSquared();
        }

        /// <summary>
        /// Returns the square of the length of a 2-D vector.
        /// </summary>
        /// <returns>A Single value that contains the vector's squared length.</returns>
        public float LengthSquared()
        {
            return x * x + y * y;
        }
        #endregion

        #region Maximize
        /// <summary>
        /// Returns a 2-D vector that is made up of the largest components of two 2-D vectors.
        /// </summary>
        /// <param name="vector1">Source Vector2 structure.</param>
        /// <param name="vector2">Source Vector2 structure.</param>
        /// <returns>A Vector2 structure that is made up of the largest components of the two vectors.</returns>
        public static Vector2 Maximize(Vector2 vector1, Vector2 vector2)
        {
            Vector2 result;
            result.x = System.Math.Max(vector1.x, vector2.x);
            result.y = System.Math.Max(vector1.y, vector2.y);
            return result;
        }

        /// <summary>
        /// Returns a 2-D vector that is made up of the largest components of two 2-D vectors.
        /// </summary>
        /// <param name="vector">Source Vector2 structure.</param>
        public void Maximize(Vector2 vector)
        {
            this.x = System.Math.Max(this.x, vector.x);
            this.y = System.Math.Max(this.y, vector.y);
        }
        #endregion

        #region Minimize
        /// <summary>
        /// Returns a 2-D vector that is made up of the smallest components of two 2-D vectors.
        /// </summary>
        /// <param name="vector1">Source Vector2 structure.</param>
        /// <param name="vector2">Source Vector2 structure.</param>
        /// <returns>A Vector2 structure that is made up of the smallest components of the two vectors.</returns>
        public static Vector2 Minimize(Vector2 vector1, Vector2 vector2)
        {
            Vector2 result;
            result.x = System.Math.Min(vector1.x, vector2.x);
            result.y = System.Math.Min(vector1.y, vector2.y);
            return result;
        }

        /// <summary>
        /// Returns a 2-D vector that is made up of the smallest components of two 2-D vectors.
        /// </summary>
        /// <param name="vector">Source Vector2 structure.</param>
        public void Minimize(Vector2 vector)
        {
            this.x = System.Math.Min(this.x, vector.x);
            this.y = System.Math.Min(this.y, vector.y);
        }
        #endregion

        #region Multiply
        /// <summary>
        /// Determines the dot product of two 2-D vectors.
        /// </summary>
        /// <param name="vector1">Source Vector2 structure.</param>
        /// <param name="vector2">Source Vector2 structure.</param>
        /// <returns>A Single value that is the dot product.</returns>
        public static float Multiply(Vector2 vector1, Vector2 vector2)
        {
            return vector1 * vector2;
        }

        /// <summary>
        /// Determines the product of a single value and a 2-D vector.
        /// </summary>
        /// <param name="vector">Source 2D vector.</param>
        /// <param name="value">Source single value.</param>
        /// <returns>Product of a 2D vector and a single value.</returns>
        public static Vector2 Multiply(Vector2 vector, float value)
        {
            return vector * value;
        }
        #endregion

        #region Negate
        /// <summary>
        /// Calculates the Negatate of a 2D vector.
        /// </summary>
        /// <param name="vector">Source Vector2 vector.</param>
        /// <returns>Negated Vector2 vector.</returns>
        public static Vector2 Negate(Vector2 vector)
        {
            return -vector;
        }

        /// <summary>
        /// Negates this 2D vector.
        /// </summary>
        public void Negate()
        {
            this = -this;
        }
        #endregion

        #region Normalize
        /// <summary>
        /// Returns the normalized version of a 2-D vector.
        /// </summary>
        public void Normalize()
        {
            float length = Length();
            x /= length;
            y /= length;
        }

        /// <summary>
        /// Returns the normalized version of a 2-D vector.
        /// </summary>
        /// <param name="vector">Source Vector2 structure.</param>
        /// <returns>A Vector2 structure that is the normalized version of the specified vector.</returns>
        public static Vector2 Normalize(Vector2 vector)
        {
            float length = vector.Length();
            return new Vector2(vector.x / length, vector.y / length);
        }
        #endregion

        #region Rotation
        /// <summary>
        /// Rotate the Vector2 vector about an angle in radian. Rotation is counter-clockwise for positive values and clockwise for negative values for the angle.
        /// </summary>
        /// <param name="vector">Source Vector2 vector.</param>
        /// <param name="angle">Source single angle in radian.</param>
        /// <returns>Rotated vector.</returns>
        public static Vector2 Rotate(Vector2 vector, float angle)
        {
            Vector2 result = new Vector2();
            result.X = vector.x * (float)Math.Cos(angle) - vector.y * (float)Math.Sin(angle);
            result.Y = vector.x * (float)Math.Sin(angle) + vector.y * (float)Math.Cos(angle);
            return result;
        }

        /// <summary>
        /// Rotate the Vector2 vector about an angle in radian. Rotation is counter-clockwise for positive values and clockwise for negative values for the angle
        /// </summary>
        /// <param name="angle">Source single angle in radian.</param>
        public void Rotate(float angle)
        {
            this = Vector2.Rotate(this, angle);
        }
        #endregion

        #region Scale
        /// <summary>
        /// Scales a 2-D vector.
        /// </summary>
        /// <param name="vector">Source Vector2 structure.</param>
        /// <param name="factor">Scaling value.</param>
        /// <returns></returns>
        public static Vector2 Scale(Vector2 vector, float factor)
        {
            Vector2 result;
            result = vector * factor;
            return result;
        }

        /// <summary>
        /// Scales a 2-D vector.
        /// </summary>
        /// <param name="factor">Scaling value.</param>
        public void Scale(float factor)
        {
            this = this * factor;
        }
        #endregion

        #region Subtraction
        /// <summary>
        /// Subtracts two 2-D vectors.
        /// </summary>
        /// <param name="vector">Source Vector2 structure to subtract from the current instance.</param>
        public void Substract(Vector2 vector)
        {
            this = this - vector;
        }

        /// <summary>
        /// Subtracts two 2-D vectors.
        /// </summary>
        /// <param name="vector1">Source Vector2 structure to the left of the subtraction operator.</param>
        /// <param name="vector2">Source Vector2 structure to the right of the subtraction operator.</param>
        /// <returns>A Vector2 structure that is the result of the operation.</returns>
        public static Vector2 Subtract(Vector2 vector1, Vector2 vector2)
        {
            return vector1 - vector2;
        }
        #endregion

        #region ToArray
        /// <summary>
        /// Returns a float array with two components: array[0] = x, array[1] = y
        /// </summary>
        /// <returns></returns>
        public float[] toArray()
        {
            float[] array = new float[2];
            array[0] = x;
            array[1] = y;
            return array;
        }
        #endregion

        #region ToString
        /// <summary>
        /// Obtains a string representation of the current instance.
        /// </summary>
        /// <returns>String that represents the object.</returns>
        public override string ToString()
        {
            return "\nx: " + x.ToString() + "\ny: " + y.ToString();
        }
        #endregion

        #region TransformCoordinate
        /// <summary>
        /// Transforms a 2-D vector or an array of 2-D vectors by a given matrix.
        /// </summary>
        /// <param name="matrix"></param>
        public void TransformCoordinate(Matrix matrix)
        {
            Vector3 v = new Vector3(this.X, this.Y, 0);
            v.TransformCoordinate(matrix);

            this.X = v.X;
            this.Y = v.Y;
        }

        public static Vector2 TransformCoordinate(Vector2 vector, Matrix matrix)
        {
            vector.TransformCoordinate(matrix);
            return vector;
        }
        #endregion

        public Vector2 OrthoRight()
        {
            return new Vector2(this.y, -this.x);
        }

        public Vector2 OrthoLeft()
        {
            return new Vector2(-this.y, this.x);
        }

        public Vector2 OrthoRightNormal()
        {
            Vector2 v = new Vector2(this.y, -this.x);
            v.Normalize();
            return v;

        }

        public Vector2 OrthoLeftNormal()
        {
            Vector2 v = new Vector2(-this.y, this.x);
            v.Normalize();
            return v;
        }

        // Operators

        #region op_Addition
        /// <summary>
        /// Adds two 2-D vectors
        /// </summary>
        /// <param name="left">Source Vector2 struct.</param>
        /// <param name="right">Source Vector2 struct.</param>
        /// <returns></returns>
        public static Vector2 operator +(Vector2 left, Vector2 right)
        {
            return new Vector2(left.x + right.x, left.y + right.y);
        }
        #endregion

        #region op_Equality
        /// <summary>
        /// Compares the current instance of a class to another instance to determine whether they are the same.
        /// </summary>
        /// <param name="vector1">The Vector2 structure to the left of the equality operator.</param>
        /// <param name="vector2">The Vector2 structure to the right of the equality operator.</param>
        /// <returns></returns>
        public static bool operator ==(Vector2 left, Vector2 right)
        {
            return (left.x == right.x && left.y == right.y);
        }
        #endregion

        #region op_Inequality
        /// <summary>
        /// Compares the current instance of a class to another instance to determine whether they are different.
        /// </summary>
        /// <param name="left">The Vector2 structure to the left of the inequality operator.</param>
        /// <param name="right">The Vector2 structure to the right of the inequality operator.</param>
        /// <returns></returns>
        public static bool operator !=(Vector2 left, Vector2 right)
        {
            return !(left.x == right.x && left.y == right.y);
        }
        #endregion

        #region op_Multiply
        /// <summary>
        /// Determines the dot product of two 2-D vectors.
        /// </summary>
        /// <param name="vector1">Source Vector2 structure.</param>
        /// <param name="vector2">Source Vector2 structure.</param>
        /// <returns>A Single value that is the dot product.</returns>
        public static float operator *(Vector2 vector1, Vector2 vector2)
        {
            return vector1.x * vector2.x + vector1.y * vector2.y;
        }

        /// <summary>
        /// Determines the product of a single value and a 2-D vector.
        /// </summary>
        /// <param name="vector">Source 2D vector.</param>
        /// <param name="value">Source single value.</param>
        /// <returns>Product of a 2D vector and a single value.</returns>
        public static Vector2 operator *(Vector2 vector, float value)
        {
            return new Vector2(vector.x * value, vector.y * value);
        }

        /// <summary>
        /// Determines the product of a single value and a 2-D vector.
        /// </summary>
        /// <param name="vector">Source 2D vector.</param>
        /// <param name="value">Source single value.</param>
        /// <returns>Product of a 2D vector and a single value.</returns>
        public static Vector2 operator *(float value, Vector2 vector)
        {
            return new Vector2(vector.x * value, vector.y * value);
        }
        #endregion

        #region op_Subtraction
        /// <summary>
        /// Subtracts two 2-D vectors.
        /// </summary>
        /// <param name="vector1">Source Vector2 structure to the left of the subtraction operator.</param>
        /// <param name="vector2">Source Vector2 structure to the right of the subtraction operator.</param>
        /// <returns>A Vector2 structure that is the result of the operation.</returns>
        public static Vector2 operator -(Vector2 vector1, Vector2 vector2)
        {
            return new Vector2(vector1.x - vector2.x, vector1.y - vector2.y);
        }
        #endregion

        #region op_UnaryNegation
        /// <summary>
        /// Negates a 2-D vector.
        /// </summary>
        /// <param name="vector1">Source Vector2 structure to the left of the subtraction operator.</param>
        /// <returns>A Vector2 structure that is the result of the operation.</returns>
        public static Vector2 operator -(Vector2 vector)
        {
            return new Vector2(-vector.x, -vector.y);
        }
        #endregion

    }
}
